package com.vilen.basic.dao;

import com.vilen.basic.model.Pager;
import com.vilen.basic.model.SystemContext;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;

import javax.inject.Inject;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * Created by vilen on 2015/8/23.
 */
public class BaseDao<T> implements IBaseDao<T> {
    /**
     * hibernateDaoSupport 只支持到3
     * 4用 sessionFactory 注入
     * 没有和spring整合更干净
     * @param t
     * @return
     */
    private SessionFactory sessionFactory;

    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }
    //jsr303 认证 JSR250 Resource jsr330 inject代替resource

    @Inject
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    protected Session getSession() {
        return sessionFactory.getCurrentSession();
        //事务管理不用管，opensessioninview来管理
    }

    /**
     * 创建一个Class 的对象来获取泛型的class
     */
    private Class<T> clz;
    public Class<T> getClz() {
        if(clz == null) {
            //获取泛型的class对象
            clz= ((Class<T>)((ParameterizedType)(this.getClass().getGenericSuperclass())).getActualTypeArguments()[0]);
        }
        return clz;
    }
    @Override
    public T add(T t) {
        getSession().save(t);
        return t;
    }
    @Override
    public void update(T t) {
        getSession().update(t);
    }
    @Override
    public void delete(int id) {
        getSession().delete(this.load(id));
    }
    @Override
    public T load(int id) {
        return (T)getSession().load(getClz(),id);
    }

    public List<T> list(String hql, Object[] args) {
        return this.list(hql,args,null);
    }

    public List<T> list(String hql, Object arg) {
        return this.list(hql,new Object[]{arg});
    }

    public List<T> list(String hql) {
        return this.list(hql,null);
    }

    private String initSort(String hql) {
        String order = SystemContext.getOrder();
        String sort = SystemContext.getSort();
        if(sort != null && !"".equals(sort.trim())) {
            hql += " order by "+sort;
            if(!"desc".equals(order)) {
                hql += " asc";
            }else {
                hql += " desc";
            }
        }
        return hql;
    }
    private void setAliasParameter(Query query,Map<String,Object> alias) {
        if(alias != null){
            Set<String> keys = alias.keySet();

            for(String key: keys) {
                Object val = alias.get(key);
                if(val instanceof Collection) {
                    //查询条件是列表
                    query.setParameterList(key,(Collection)val);
                }else {
                    query.setParameter(key,val);
                }
            }
        }
    }
    private void setParameter(Query query,Object[] args) {
        if(args!=null && args.length>0) {
            int index = 0;
            for(Object arg: args) {
                query.setParameter(index++,arg);
            }
        }
    }


    public List<T> list(String hql, Object[] args, Map<String, Object> alias) {
        hql = initSort(hql);

        Query query = getSession().createQuery(hql);
        //先别名。要不然会出错
        setAliasParameter(query, alias);
        //2
        setParameter(query,args);
        return query.list();
    }

    public List<T> listByAlias(String hql, Map<String, Object> alias) {
        return this.list(hql, null, alias);
    }

    public Pager<T> find(String hql, Object[] args) {
        return this.find(hql,args,null);
    }

    public Pager<T> find(String hql, Object arg) {
        return this.find(hql,new Object[]{arg});
    }

    public Pager<T> find(String hql) {
        return this.find(hql,null);
    }
    private void setPagers(Query query,Pager pages) {
        Integer pageSize = SystemContext.getPageSize();
        Integer pageOffset = SystemContext.getPageOffset();
        if(pageOffset == null || pageOffset<0) {
            pageOffset = 0;
        }
        if(pageSize == null || pageSize <0) {
            pageSize = 15;
        }
        pages.setOffset(pageOffset);
        pages.setSize(pageSize);
        query.setFirstResult(pageOffset).setMaxResults(pageSize);
    }

    private String getCountHql(String hql,boolean isHql) {
        String e = hql.substring(hql.indexOf("from"));
        String c = "select count(*) "+e;
        if(isHql) {
            c.replaceAll("fetch", "");
        }
        return c;
    }


    public Pager<T> find(String hql, Object[] args, Map<String, Object> alias) {
        hql = initSort(hql);
        String cq = getCountHql(hql,true);

        Query cquery = getSession().createQuery(cq);
        Query query = getSession().createQuery(hql);
        //设置别名
        setAliasParameter(query,alias);
        setAliasParameter(cquery,alias);
        //设置参数
        setParameter(query,args);
        setParameter(cquery,args);

        Pager<T> pages = new Pager<T>();
        setPagers(query,pages);

        List<T> datas = query.list();
        pages.setDatas(datas);

        long total = (Long)cquery.uniqueResult();
        pages.setTotal(total);
        return pages;
    }

    public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
        return this.find(hql,null,alias);
    }

    public Object queryObject(String hql, Object[] args) {
        return this.queryObject(hql,args,null);
    }

    public Object queryObject(String hql, Object arg) {
        return this.queryObject(hql,new Object[]{arg});
    }

    public Object queryObject(String hql) {
        return this.queryObject(hql,null);
    }


    public Object queryObject(String hql, Object[] args, Map<String, Object> alias) {
        Query query = getSession().createQuery(hql);
        setAliasParameter(query,alias);
        setParameter(query,args);

        return query.uniqueResult();
    }


    public Object queryObjectByAlias(String hql, Map<String, Object> alias) {
        return queryObject(hql,null,alias);
    }


    public void updateByHql(String hql, Object[] args) {
        Query query = getSession().createQuery(hql);
        setParameter(query,args);
        query.executeUpdate();
    }

    public void updateByHql(String hql, Object arg) {
        this.updateByHql(hql,new Object[]{arg});
    }

    public void updateByHql(String hql) {
        this.updateByHql(hql,null);
    }

    public <N extends Object>List<N> listBySql(String sql, Object[] args, Class<? extends Object> clz, boolean hasEntity) {

        return this.listBySql(sql,args,null,clz,hasEntity);
    }

    public <N extends Object>List<N> listBySql(String sql, Object arg, Class<? extends Object> clz, boolean hasEntity) {
        return this.listBySql(sql,new Object[]{arg},clz,hasEntity);
    }

    public <N extends Object>List<N> listBySql(String sql, Class<? extends Object> clz, boolean hasEntity) {
        return this.listBySql(sql,null,clz,hasEntity);
    }

    public <N extends Object>List<N> listBySql(String sql, Object[] args, Map<String, Object> alias, Class<? extends Object> clz, boolean hasEntity) {
        sql = initSort(sql);
        SQLQuery sqlQuery = getSession().createSQLQuery(sql);
        setAliasParameter(sqlQuery,alias);
        setParameter(sqlQuery,args);
        if(hasEntity) {
            sqlQuery.addEntity(clz);
        }else {
            sqlQuery.setResultTransformer(Transformers.aliasToBean(clz));
        }
        return sqlQuery.list();
    }

    public <N extends Object>List<N> listByAliasSql(String sql, Map<String, Object> alias, Class<? extends Object> clz, boolean hasEntity) {
        return this.listBySql(sql,null,alias,clz,hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Object[] args, Class<? extends Object> clz, boolean hasEntity) {
        return this.findBySql(sql,args,null,clz,hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Object arg, Class<? extends Object> clz, boolean hasEntity) {
        return this.findBySql(sql,new Object[]{arg},clz,hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Class<? extends Object> clz, boolean hasEntity) {
        return this.findBySql(sql,null,clz,hasEntity);
    }

    public <N extends Object>Pager<N> findBySql(String sql, Object[] args, Map<String, Object> alias, Class<? extends Object> clz, boolean hasEntity) {
        String cq = getCountHql(sql, false);
        sql = initSort(sql);
        SQLQuery cquery = getSession().createSQLQuery(cq);
        SQLQuery query  = getSession().createSQLQuery(sql);
        setAliasParameter(cquery, alias);
        setAliasParameter(query, alias);

        setParameter(cquery, args);
        setParameter(query, args);

        Pager<N> pages = new Pager<N>();
        setPagers(query,pages);
        if(hasEntity){
            query.addEntity(clz);
        }else {
            query.setResultTransformer(Transformers.aliasToBean(clz));
        }
        List<N> datas = query.list();
        pages.setDatas(datas);

        long total = ((BigInteger)cquery.uniqueResult()).longValue();
        pages.setTotal(total);
        return pages;
    }

    public <N extends Object>Pager<N> findByAliasSql(String sql, Map<String, Object> alias, Class<? extends Object> clz, boolean hasEntity) {
        return this.findBySql(sql,null,alias,clz,hasEntity);
    }
}
